/**
 * 网页模板是把动态的数据和静态的表现组装到一起的工具，使得内容与表现方式可以分离，是Web开发中的重要技术手段。
 * 早期的Web开发很简单，没有“模板 ”的概念。
 * 只需把数据提取出来，放在HTML里面显示就达到程序的目的。
 * HTML代码混合于逻辑代码之中，HTML就是直接显示的内容，内嵌在HMTL 中<% ... %>表示为后台(服务端)执行代码，
 * 但很多情况<% ... %>中又有HTML的片段，至于怎么拼凑出HTML片段的方式方法各式各样、与多种的后台语言掺杂一起(ASP、PHP、JSP)各显神通。
 */
$$.tpl = (function(){
	// 匹配闭合tag的正则。
	var divBlock = '<{0}\\s*[^>]*>((?:(?=([^<]+))\\2|<(?!{0}\\s*[^>]*>))*?)<\/({0})>';
	var matchValue = (function(){
		var
		 regexp    = /{([^}\n]*)}/ig
		,execJS	   = /^\[([^\]]*)\]$/
		,getParent = /^parent\.\b(\w*)\b$/i
		,getRoot   = /^root\.\b(\w*)\b$/i;
	
	/**
	 * @private
	 * @param {mixed} v
	 * @return {Boolean} 是否hash（是否对象）。
	 */
	function isObject(v){
		return !!v && isObject.toString.call(v) == isObject.token;
	}
	isObject.toString = Object.prototype.toString
	isObject.token    = '[object Object]';
	
 	function replace(m1, m2){
		var 
		 jsCode
		,parent  = arguments.callee.parent 
		,root	 = arguments.callee.root;
			
		if(execJS.test(m2)){
			
			jsCode = execJS.exec(m2);
			jsCode = jsCode[1];
			
			try{ // 写try之目的为容错性的设计。
				with(this){
					jsCode = eval(jsCode);
				}
				return jsCode;
			}catch(e){
				return '空值';
			}
		}else if(isObject(parent) && getParent.test(m2)){ /* 父级寻址 */
			m2 = m2.match(getParent);
			m2 = m2[1];
			return parent[m2] || m1;// 替换置，如果未发现匹配的值，就返回原模版的格式（{xxx}），即m1
		}else if(isObject(root) && getRoot.test(m2)){ /* 全称寻址 */
			m2 = m2.match(getRoot);
			m2 = m2[1];
			return root[m2]   || m1;
		}else{
			return this[m2]   || m1;
		}
	}
	
	 /**
	  * Lazy function
	  * @param {String} tpl
	  * @param {Object} dataObj
	  * @param {Object} parentObj
	  * @return {String}
	  */
	return function(tpl, dataObj, parentObj){
		if(!replace.root){
			replace.root = $$.tpl.root;
		}
		
		replace.parent = parentObj || 'itself'; // 无else部分，即表示全部就是trueBlock
		replace.scope  = dataObj;
		return tpl.replace(regexp, replace.delegate());// set scope object!
	}
	})();
	
	var matchIf = (function(){
		var 
		 elseReg = /<else\s*\/?>/i
		,evalReg = /<if\s*(?:eval="([\w\d=<>\.!'\|\(\)]*)")[^>]*>/i
		,tabReg  = /\t{2}/g
		,ifReg   = new RegExp(divBlock.format('if'), 'ig');
	
	/**
	 * 运算<if eval="exp">内容。
	 * @param {Object} data
	 * @param {Object} parent
	 * @param {String} ifTag
	 * @return {Boolean}
	 */
	function evalExp(data, parent, ifTag){
		var exp;
			 
		if(!evalReg.test(ifTag)){
			$$.console.error('输入的标签{0}不是一个标准的if tag'.format(ifTag));
			throw '不是一个标准的if tag';
		}
		exp = evalReg.exec(ifTag);
		exp = exp[1];
		exp = '(' + exp +')'; // make it as the Expression 表达式
//		debugger;
		// 通过with(data){}给定对象可见的范围。如果没有matchedObj，则为this。这也是有可能的。
		with(data || parent || this){
			try{
				exp = !!eval(exp);
			}catch(e){
				exp = false;
			}
		}
		return exp;
	}
	
	/**
	 * @this {OBject} 值对象
	 */
	function replace(m1, m2){
		var 
		 evalResult 		// 运算表达式后的结果
		,trueBlock			// true块
		,falseBlock = null	// false块，默认没有
			
		if(elseReg.test(m2)){// 有else部分
			var arr = m2.split(elseReg);
			if(!arr || arr.length < 2){
				$$.console.error('if-else不完整');
				throw "if-else不完整";
			};
			 
			trueBlock  = arr[0];
			falseBlock = arr[1];
		}else{
			trueBlock  = m2; // 无else部分，即表示全部就是trueBlock
		}
		
		trueBlock  = String.trim(trueBlock);
		trueBlock  = trueBlock.replace(tabReg, '\t');// 消除多余tab符。
		// 求 if 的表达式
		evalResult = evalExp(this, arguments.callee.parent, m1);
		
		if(evalResult){
			return trueBlock;
		}else if(!evalResult && falseBlock == null){
			return '';
		}else if(!evalResult && falseBlock){
			return falseBlock;
		}else{
			// 不应该会走到这里的。若真的走到便抛出一个异常。
			$$.console.error('求if-else块时发生致命错误！');
			throw '求if-else块时发生致命错误！';							
		}
	}
	
	return function(tpl, data, parentData){
		ifReg.lastIndex = 0;// 有global时需要注意 lastIndex的问题。
		
		if(ifReg.test(tpl)){
			replace.parent = parentData;
			replace.scope  = data;
			return tpl.replace(ifReg, replace.delegate());
		}else{
			return tpl;
		}
	}
	})();

	function replace(m1, m2, m3, m4){
		var 
	     parentData = this
		,values		= parentData[m4]
		,callee		= $$.tpl.fillData
    	,str 		= ''
		,arrTpl;
	    
	    if(values && values.pop){ /* 有时候会因为大小写问题，无法匹配。请检查key即（m4）是否一致 */
	        m2 = callee(m2, values); // 递归 for sub
        		
			for(var i = 0, j = values.length; i < j; i++){
				value  = values[i];
				m2	   = matchIf(m2, value, parentData); 
				arrTpl =  callee(m2, value);
				
				str += matchValue(arrTpl, value, parentData);
			} 
			return str;

	    }else if(values == false /* 特定为false的情况 */ ){
	    	return str;
	    }else{
	    	$$.console.warn('怎么数据源没提供数据？ 宜debug之');
	    	return 'nothing';// 怎么数据源没提供数据？ 宜debug之
	    }
	}
	
return {
	
	/**
	 * @param {String} tpl
	 * @param {Object} data
	 * @return {String}
	 */
	fillData : function (tpl, data){
		if(!$$.tpl.root){
			$$.tpl.root = data;
		}

	    if(data && !data.pop){
	    	replace.scope = data;
	    	var _replace = replace.delegate();
	    	
	    	for(var i in data){
				tpl = tpl.replace(new RegExp(divBlock.format(i), 'i'), _replace);
	    	}
	    	
	    	tpl = matchIf(tpl, data, data); 
	    	
		    return matchValue(tpl, data);
	    }else if(data && data.pop){
	    	return tpl;
	    }
	    return '';
	}
	,root : null
	
	/**
	 * 为能在HTML显示的字符转义&、<、>以及'。
	 * @return {String} 编码后的文本
	 */
	,htmlEncode : function(isAndOff){
	    var str = this.replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
	    return isAndOff ? str.replace(/&/g, "&amp;") : str;
	}
	
	/**
	 * 将&、<、>以及'字符从HTML显示的格式还原。
	 * @return {String} 编码后的文本
	 * @credit Ext
	 */
	,htmlDecode : function(v){
	    return v.replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
	}
};

	
	/**
	 * decode an XML string for use
	 * @param {String} str the encoded XML string
	 * @return {String} the decoded XML string
	 */
	var decode = function(str){
		str = str.replace(/&amp;/g, 	'&');
		str = str.replace(/&lt;/g, 		'<');
		str = str.replace(/&gt;/g,		'>');
		str = str.replace(/&quot;/g,	'"');
		str = str.replace(/&#039;/g,	'\'');
		return str
	},
	
	/**
	 * encode an XML string for transfer
	 * @param {String} str the XML string
	 * @return {String} the encoded XML string
	 */
	encode = function(str){
		str = str.replace(/&/g, '&amp;');
		str = str.replace(/</g, '&lt;');
		str = str.replace(/>/g, '&gt;');
		str = str.replace(/"/g, '&quot;');
//		.replace(/"/g, /* "-->\x22 ? */ "&quot;")	// 转义双引号
		str = str.replace(/'/g, '&apos;');
		return str;
	}
})();